#pragma once

#ifndef __HWINIO_H__
#define __HWINIO_H__

#include "hwinstring.h"
#include "hwindatetime.h"
#include "hwinobject.h"

namespace harlinn
{
    namespace windows
    {
        namespace io
        {

            class StreamBase : public Object
            {
            protected:    
                StreamBase() 
                {
                    HWIN_TRACE();
                }
                HWIN_EXPORT virtual ComObject* CreateComObject() const;
            public:
                HWIN_EXPORT StreamBase& Initialize();


                typedef Object Base;
                HWIN_EXPORT long long Position() const;
                HWIN_EXPORT virtual StreamBase& SetPosition(long long thePosition);

                HWIN_EXPORT virtual long long Size() const;
                HWIN_EXPORT virtual StreamBase& SetSize(long long theSize);

                HWIN_EXPORT virtual StreamBase& Flush();

                virtual long long Read(void* buffer, size_t numberOfBytesToRead ) = 0;
                virtual long long Write(const void* buffer, size_t numberOfBytesToWrite ) = 0;
                virtual long long Seek(long long offset, SeekOrigin seekOrigin) = 0;
                virtual std::shared_ptr<StreamBase> Clone() const = 0;
            };

			




            enum class FileAttributes
            {
                Readonly            = 0x00000001,
                Hidden              = 0x00000002,
                System              = 0x00000004,
                Directory           = 0x00000010,
                Archive             = 0x00000020,
                Device              = 0x00000040,
                Normal              = 0x00000080,
                Temporary           = 0x00000100,
                SparseFile          = 0x00000200,
                ReparsePoint        = 0x00000400,
                Compressed          = 0x00000800,
                Offline             = 0x00001000,
                NotContentIndexed   = 0x00002000,
                Encrypted           = 0x00004000,
                IntegrityStream     = 0x00008000,
                Virtual             = 0x00010000,
                NoScrubData         = 0x00020000  
            };
            DEFINE_ENUM_FLAG_OPERATORS(FileAttributes)

            enum class FileAccess
            {
                // Read access to the file. Data can be read from the file. Combine with Write
                // for read/write access.
                Read = FILE_READ_ACCESS,
                // Write access to the file. Data can be written to the file. Combine with Read
                // for read/write access.
                Write = FILE_WRITE_ACCESS,
                // Read and write access to the file. Data can be written to and read from the file.
                ReadWrite = FILE_READ_ACCESS | FILE_WRITE_ACCESS,
                // the right to append data to the file
                Append = FILE_APPEND_DATA,
                // The right to read extended file attributes
                ReadExtendedAttributes = FILE_READ_EA,
                // The right to write extended file attributes
                WriteExtendedAttributes = FILE_WRITE_EA,
                // The right to read file attributes
                ReadAttributes = READ_ATTRIBUTES,
                // The right to set file attributes
                WriteAttributes = FILE_WRITE_ATTRIBUTES,

                StandardRightsRead = STANDARD_RIGHTS_READ,
                StandardRightsWrite = STANDARD_RIGHTS_WRITE,
                Default = FILE_ALL_ACCESS
            };
            DEFINE_ENUM_FLAG_OPERATORS(FileAccess)

            enum class FileMode
            {
                // Specifies that the operating system should create a new file. 
                CreateNew = 1,
                // Specifies that the operating system should create a new file. If the file
                // already exists, it will be overwritten. 
                Create = 2,
                // Specifies that the operating system should open an existing file. The ability
                // to open the file is dependent on the value specified by the FileAccess
                // enumeration.
                Open = 3,
                // Specifies that the operating system should open a file if it exists; otherwise,
                // a new file should be created. 
                OpenOrCreate = 4,
                // Specifies that the operating system should open an existing file. When the
                // file is opened, it should be truncated so that its size is zero bytes.
                Truncate = 5,
                // Opens the file if it exists and seeks to the end of the file, or creates
                // a new file.
                Append = 6,
                Default = OpenOrCreate
            };


            enum class FileShare
            {
                // Declines sharing of the current file. Any request to open the file (by this
                // process or another process) will fail until the file is closed.
                None = 0,
                // Allows subsequent opening of the file for reading. If this flag is not specified,
                // any request to open the file for reading (by this process or another process)
                // will fail until the file is closed.
                Read = 1,
                // Allows subsequent opening of the file for writing. If this flag is not specified,
                // any request to open the file for writing (by this process or another process)
                // will fail until the file is closed.
                Write = 2,
                // Allows subsequent opening of the file for reading or writing. If this flag
                // is not specified, any request to open the file for reading or writing (by
                // this process or another process) will fail until the file is closed.
                ReadWrite = 3,
                // Allows subsequent deleting of a file.
                Delete = 4,
                // Makes the file handle inheritable by child processes. 
                Inheritable = 16,
                Default = None
            };
            DEFINE_ENUM_FLAG_OPERATORS(FileShare)

            enum class FileOptions : DWORD 
            {
                // Indicates that the system should write through any intermediate cache and
                // go directly to disk.
                WriteThrough = FILE_FLAG_WRITE_THROUGH,
                // Indicates no additional parameters.
                None = 0,
                // Indicates that a file is encrypted and can be decrypted only by using the
                // same user account used for encryption.
                Encrypted = 0x00004000,
                // Indicates that a file is automatically deleted when it is no longer in use.
                DeleteOnClose = FILE_FLAG_DELETE_ON_CLOSE,
                // Indicates that the file is to be accessed sequentially from beginning to
                // end. The system can use this as a hint to optimize file caching. If an application
                // moves the file pointer for random access, optimum caching may not occur;
                // however, correct operation is still guaranteed.
                SequentialScan = FILE_FLAG_SEQUENTIAL_SCAN,
                // Indicates that the file is accessed randomly. The system can use this as
                // a hint to optimize file caching.
                RandomAccess = FILE_FLAG_RANDOM_ACCESS,
                // Indicates that a file can be used for asynchronous reading and writing.
                Asynchronous = FILE_FLAG_OVERLAPPED,
                Default = FILE_FLAG_SEQUENTIAL_SCAN
            };
            DEFINE_ENUM_FLAG_OPERATORS(FileOptions)

            class DirectoryInfo;
            class FileSystemEntry
            {
                String parentDirectory;
                String name;
                FileAttributes attributes;
                DateTime creationTime;
                DateTime lastAccessTime;
                DateTime lastWriteTime;
                
            public:
                HWIN_EXPORT FileSystemEntry(const String& theParentDirectory, const String& theName, FileAttributes theAttributes, 
                    const DateTime& theCreationTime, const DateTime& theLastAccessTime, const DateTime& lastWriteTime);


                String ParentDirectory() const
                {
                    return parentDirectory;
                }
                String Name() const
                {
                    return name;
                }

                FileAttributes Attributes()
                {
                    return attributes;
                }

                DateTime CreationTime() const
                {
                    return creationTime;
                }
                DateTime LastAccessTime() const
                {
                    return lastAccessTime;
                }
                DateTime LastWriteTime() const
                {
                    return lastWriteTime;
                }

            };

            class FileInfo : public FileSystemEntry
            {
                long long fileSize;
            public:
                typedef FileSystemEntry Base;
                HWIN_EXPORT FileInfo(const String& theParentDirectory, const String& theName, FileAttributes theAttributes, 
                    const DateTime& theCreationTime, const DateTime& theLastAccessTime, const DateTime& lastWriteTime,
                    long long theFileSize);
            };


            class DirectoryInfo : public FileSystemEntry
            {
            public:
                typedef FileSystemEntry Base;

                HWIN_EXPORT DirectoryInfo(const String& theParentDirectory, const String& theName, FileAttributes theAttributes, 
                    const DateTime& theCreationTime, const DateTime& theLastAccessTime, const DateTime& lastWriteTime);
            };


            class FileSystemEntriesBase
            {
                String path;
                String searchPattern;
                HANDLE searchHandle;
            protected:
                WIN32_FIND_DATAW data;
                std::shared_ptr<FileSystemEntry> current;
            public:
                HWIN_EXPORT FileSystemEntriesBase(const String& thePath, const String& theSearchPattern );
                HWIN_EXPORT FileSystemEntriesBase(const String& theSearchPattern );
                HWIN_EXPORT ~FileSystemEntriesBase();

                HWIN_EXPORT bool Read();

                HWIN_EXPORT String Name() const;
                HWIN_EXPORT String FullPath() const;


            };



            class Path
            {
            public:
                static const wchar_t DirectorySeparatorChar = '\\';
                static const wchar_t AltDirectorySeparatorChar = '/';
                static const wchar_t VolumeSeparatorChar = ':'; 
                static const wchar_t PathSeparator = ';'; 

                static const int MaxPath = 260;
                static const int MaxDirectoryLength = 255; 

                HWIN_EXPORT static String ChangeExtension(const String& path, const String& newExtension); 
                HWIN_EXPORT static String GetLongPathName(const String& path);
                HWIN_EXPORT static String GetFullPathName(const String& path);
                HWIN_EXPORT static String GetFullPathName(const String& path,String::size_type& indexOfFileName);
                

            };


            class File
            {
            public:
                HWIN_EXPORT static bool Exist(const String& filePath);
                HWIN_EXPORT static bool Exist(const wchar_t* filePath);
            };


            class Directory
            {
            public:
                HWIN_EXPORT static String GetCurrentDirectory();
                HWIN_EXPORT static bool Exist(const String& directoryPath);
                HWIN_EXPORT static bool Exist(const wchar_t* filePath);
            };



            class StreamCore : public StreamBase
            {
                HANDLE hFile;
            public:
                typedef StreamBase Base;

                HWIN_EXPORT explicit StreamCore(HANDLE theFileHandle);
                HWIN_EXPORT ~StreamCore();


                HWIN_EXPORT virtual StreamBase& SetPosition(long long thePosition);

                HWIN_EXPORT virtual long long Size() const;
                HWIN_EXPORT virtual StreamBase& SetSize(long long theSize);

                HWIN_EXPORT virtual StreamBase& Flush();

                HWIN_EXPORT virtual long long Read(void* buffer, size_t numberOfBytesToRead );
                HWIN_EXPORT virtual long long Write(const void* buffer, size_t numberOfBytesToWrite );
                HWIN_EXPORT virtual long long Seek(long long offset, SeekOrigin seekOrigin);
                HWIN_EXPORT virtual std::shared_ptr<StreamBase> Clone() const;
            };


            class FileStream : public StreamCore
            {
                HWIN_EXPORT HANDLE Create(LPCWSTR lpFileName, FileAccess fileAccess, FileShare fileShare, LPSECURITY_ATTRIBUTES lpSecurityAttributes,FileMode fileMode,FileAttributes attributes,FileOptions fileOptions);
            public:
                typedef StreamCore Base;

                HWIN_EXPORT explicit FileStream(HANDLE theFileHandle);
                HWIN_EXPORT FileStream(const String& fileName, FileAccess fileAccess, FileShare fileShare, LPSECURITY_ATTRIBUTES lpSecurityAttributes,FileMode fileMode,FileAttributes attributes,FileOptions fileOptions);
                HWIN_EXPORT FileStream(LPCWSTR fileName, FileAccess fileAccess, FileShare fileShare, LPSECURITY_ATTRIBUTES lpSecurityAttributes,FileMode fileMode,FileAttributes attributes,FileOptions fileOptions);

                HWIN_EXPORT FileStream(const String& fileName);
                HWIN_EXPORT FileStream(LPCWSTR fileName);

                HWIN_EXPORT FileStream(const String& fileName,FileAccess fileAccess);
                HWIN_EXPORT FileStream(LPCWSTR fileName,FileAccess fileAccess);

                HWIN_EXPORT FileStream(const String& fileName,FileAccess fileAccess,FileShare fileShare);
                HWIN_EXPORT FileStream(LPCWSTR fileName,FileAccess fileAccess,FileShare fileShare);

                HWIN_EXPORT FileStream(const String& fileName,FileAccess fileAccess,FileMode fileMode);
                HWIN_EXPORT FileStream(LPCWSTR fileName,FileAccess fileAccess,FileMode fileMode);

                HWIN_EXPORT FileStream(const String& fileName,FileAccess fileAccess,FileShare fileShare,FileMode fileMode);
                HWIN_EXPORT FileStream(LPCWSTR fileName,FileAccess fileAccess,FileShare fileShare,FileMode fileMode);

                HWIN_EXPORT FileStream(const String& fileName,FileShare fileShare,FileMode fileMode);
                HWIN_EXPORT FileStream(LPCWSTR fileName,FileShare fileShare,FileMode fileMode);

                HWIN_EXPORT FileStream(const String& fileName,FileMode fileMode);
                HWIN_EXPORT FileStream(LPCWSTR fileName,FileMode fileMode);


                HWIN_EXPORT FileStream(const String& fileName, FileAccess fileAccess, FileShare fileShare, FileMode fileMode,FileAttributes attributes,FileOptions fileOptions);
                HWIN_EXPORT FileStream(LPCWSTR fileName, FileAccess fileAccess, FileShare fileShare, FileMode fileMode,FileAttributes attributes,FileOptions fileOptions);




            };



        };
    };
};


#endif //__HWINIO_H__
